package com.game;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.common.game.Bid;
import com.common.game.Player;
import com.common.utilities.ReceiveData;

/**
 * User: wattsc5
 * <p/>
 * Date: 2/28/12
 */
public class ClientGameAPI
{
	private static final Logger logger = Logger.getLogger(ClientGameAPI.class);

	private ClientGame game;
	private final Map<Integer, Player> players = new HashMap<Integer, Player>();

	public ClientGameAPI(ClientGame game)
	{
		this.game = game;
	}

	public void state(final ReceiveData receiveData)
	{
		String state = receiveData.getArgument(1);
		int yourPlayerId = receiveData.getArgumentAsInt(2);
		int playerCount = receiveData.getArgumentAsInt(3);

		if (state.equals("in_game"))
		{
			state = "In game";
		}
		else if (state.equals("lobby"))
		{
			state = "In lobby";
		}
		else if (state.equals("lobby_with_timer"))
		{
			state = "In lobby with timer";
		}

		String stateData = "-===Server State===-\n";
		stateData += state + "\n";
		stateData += "Your ID:" + yourPlayerId + "\nPlayers:" + playerCount + "\n";

		int actualIndex = 4;
		for (int i = 0; i < playerCount; i++)
		{
 			String playerName = receiveData.getArgument(actualIndex);
			int playerId = receiveData.getArgumentAsInt(actualIndex + 1);

			if (!this.players.containsKey(playerId))
			{
				this.players.put(playerId, new Player(playerName, playerId));
			}

			stateData += "(" + playerId + ")" + playerName + "\n";
			actualIndex += 2;
		}
		stateData += "-==================-\n";

		this.game.sendMessageToConsole(stateData);
		logger.trace("State");
	}

	public void client_joined(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("client_joined", 3, receiveData.getArgumentCount()))
		{
			return;
		}

		String playerName = receiveData.getArgument(1);
		int playerId = receiveData.getArgumentAsInt(2);

		//String clientJoined = "-===Player Joined===-\n";
		String clientJoined = "";
		clientJoined += "(" + playerId + ")" + playerName + " joined\n";
		this.game.sendMessageToConsole(clientJoined);
		logger.trace("Client joined");

		Player player = new Player(playerName, playerId);
		if (this.game.getPlayer() == null)
		{
			this.game.setPlayer(player);
			this.game.changeAIState(AIState.InLobby);
		}
		players.put(playerId, player);
	}

	public void client_quit(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("client_quit", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		int playerId = receiveData.getArgumentAsInt(1);
		//String clientQuit = "-===Player Quit===-\n";
		String clientQuit = "";
		clientQuit += players.get(playerId).getName() + " quit\n";
		this.game.sendMessageToConsole(clientQuit);
		logger.trace("Client quit");

		this.players.remove(playerId);
	}

	public void client_kicked(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("client_kicked", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		//String clientKicked = "-===Player Kicked===-\n";
		String clientKicked = "";
		int playerId = receiveData.getArgumentAsInt(1);
		clientKicked += players.get(playerId).getName() + " was kicked\n";
		this.game.sendMessageToConsole(clientKicked);
		if (this.game.getPlayer().getId() == playerId)
		{
			this.game.changeAIState(AIState.Kicked);
		}

		logger.trace("Client kicked");
	}

	public void timer_start(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("timer_start", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		this.game.sendMessageToConsole("Round will start in in " + receiveData.getArgument(1) + " seconds\n");
		logger.trace("Timer started");
	}

	public void round_start(final ReceiveData receiveData)
	{
		this.game.setLastBid(null);
		int playerCount = receiveData.getArgumentAsInt(1);
		String roundStart = "-===Round started===-\nThere are ";
		roundStart += playerCount + " players in this game\n";

		int actualIndex = 2;
		for (int i = 0; i < playerCount; i++)
		{
			int playerId = receiveData.getArgumentAsInt(actualIndex);
			String playerName = players.get(playerId).getName();
			int diceCount = receiveData.getArgumentAsInt(actualIndex + 1);

			if (diceCount == 1)
			{
				roundStart += playerName + " has " + diceCount + " die\n";
			}
			else
			{
				roundStart += playerName + " has " + diceCount + " dice\n";
			}

			actualIndex += 2;
		}

		roundStart += "-===================-\n";
		this.game.sendMessageToConsole(roundStart);
		this.game.changeAIState(AIState.InGame);
		logger.trace("Round started");
	}

	public void your_dice(final ReceiveData receiveData)
	{
		String yourDice = "";
		int diceCount = receiveData.getArgumentAsInt(1);
		List<Integer> diceValues = new ArrayList();

		if (diceCount == 1)
		{
			yourDice += "You have one die [ ";
		}
		else
		{
			yourDice += "You have " + diceCount + " dice [ ";
		}

		for (int i = 0; i < diceCount; i++)
		{
			int actualIndex = i + 2;
			int diceValue = receiveData.getArgumentAsInt(actualIndex);
			diceValues.add(diceValue);
			yourDice += diceValue + " ";
		}

		yourDice += "]\n";

		this.game.sendMessageToConsole(yourDice);
		if (this.game.getPlayer() != null)
		{
			this.game.getPlayer().setNumberOfDie(diceCount);
			this.game.getPlayer().setDiceValues(diceValues);
		}
		else
		{
			logger.warn("Tried to set player number of dice but the player was null");
		}

		logger.trace("Your dice");
	}

	public void player_turn(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("player_turn", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		int playerId = receiveData.getArgumentAsInt(1);
		Player player = this.players.get(playerId);

		if (player == null)
		{
			logger.warn("Could not get the player while notifying the players turn.");
			return;
		}

		String playersTurn = "";
		Player thisPlayer = this.game.getPlayer();
		if (thisPlayer != null &&
		    player.getId() == this.game.getPlayer().getId())
		{
			playersTurn += "Its your turn\n";
		}
		else
		{
			playersTurn += "Its " + player.getName() + "'s turn\n";
		}

		this.game.sendMessageToConsole(playersTurn);
		if (this.game.getPlayer().getId() == playerId)
		{
			this.game.changeAIState(AIState.PlayerTurn);
		}
		logger.trace("Players turn");
	}

	public void bid_report(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("bid_report", 4, receiveData.getArgumentCount()))
		{
			return;
		}

		int diceCount = receiveData.getArgumentAsInt(2);
		int diceValue = receiveData.getArgumentAsInt(3);
		int playerId = receiveData.getArgumentAsInt(1);

		String bidReport = "";

		bidReport += this.players.get(playerId).getName() +
		             " bid " + diceCount +
		             " die with value " +
		             diceValue + "\n";
		this.game.sendMessageToConsole(bidReport);
		this.game.setLastBid(new Bid(diceCount, diceValue));
		logger.trace("Bid report");
	}

	public void challenge_report(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("challenge_report", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		int playerId = receiveData.getArgumentAsInt(1);
		String playerChallenged = "";
		playerChallenged += this.players.get(playerId).getName() + " called the previous player a liar\n";
		this.game.sendMessageToConsole(playerChallenged);

		logger.trace("Challenge report");
	}

	public void round_end(final ReceiveData receiveData)
	{
		int playerLostID = receiveData.getArgumentAsInt(1);
		int playerCount = receiveData.getArgumentAsInt(2);
		String roundEnd = "-===Round over===-\n" + this.players.get(playerLostID).getName() + " lost\n";

		int actualIndex = 3;
		for (int i = 0; i < playerCount; i++)
		{
			int playerID = receiveData.getArgumentAsInt(actualIndex);
			int diceCount = receiveData.getArgumentAsInt(actualIndex + 1);
			String playerName = this.players.get(playerID).getName();

			roundEnd += playerName + " had " + diceCount + " dice [ ";

			int offset = actualIndex;
			for (int j = actualIndex; j < diceCount + offset; j++)
			{
				int diceValue = receiveData.getArgumentAsInt(j + 2);
				actualIndex++;
				roundEnd += diceValue + " ";
			}

			actualIndex += 2;
			roundEnd += "]\n";

		}
		roundEnd += "-================-\n";

		this.game.sendMessageToConsole(roundEnd);
		logger.trace("Round end");
	}

	public void game_end(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("game_end", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		int playerId = receiveData.getArgumentAsInt(1);
		String playerName = this.players.get(playerId).getName();
		this.game.sendMessageToConsole("-===Game over===-\n" + playerName + " won!\n-===============-\n");
		this.game.changeAIState(AIState.InLobby);
		logger.trace("Game end");
	}

	public void invalid_move(final ReceiveData receiveData)
	{
		if (!this.checkArgumentCount("invalid_move", 2, receiveData.getArgumentCount()))
		{
			return;
		}

		this.game.sendMessageToConsole("Invalid move. Move " + receiveData.getArgumentAsInt(1) + "\n");

		logger.trace("Invalid move");
		this.game.changeAIState(AIState.InvalidMove);
	}

	private boolean checkArgumentCount(String command, int correctNumberOfArguments, int actualNumberOfArguments)
	{
		if (actualNumberOfArguments != correctNumberOfArguments)
		{
			logger.error("Incorrect number of arguments for" + command + "(" + correctNumberOfArguments + ")" + " Actual:" + actualNumberOfArguments);
			return false;
		}

		return true;
	}
}
